module BranchTargetBuffer(

	input			i_BTB_clk,
	input			i_BTB_enable,
	input			i_BTB_reset,
	//EnQueue
	input			i_BTB_PDCvalid,
	input			i_BTB_PDClikely,
	input	[31:0]	i_BTB_PDCPC,
	input	[31:0]	i_BTB_PDCtarget,
	//Update
	input			i_BTB_CMTvalid,
	input	[1:0]	i_BTB_CMTtaken,
	input	[31:0]	i_BTB_CMTPC,
	input	[31:0]	i_BTB_CMTtarget,
	//LookUp
	input	[31:0]	i_BTB_LUPPC,
	output			o_BTB_LUPhit,
	output	[1:0]	o_BTB_LUPcounter,
	output	[31:0]	o_BTB_LUPtarget
	
);

	reg			vaild[31:0];
	reg	[31:0]	PC[31:0];
	reg	[31:0]	target[31:0];
	reg	[1:0]	counter[31:0];
	reg			likely[31:0];
	
	reg	[4:0]	EnQueuePointer;
	
	//Update
	wire	[31:0]	UpdateList;
	begin:	updatehitlist
		reg	[7:0]	tempI;
		for(tempI=8'd0; tempI<=8'd31; tempI=tempI+8'd1)
			assign	UpdateList[tempI[4:0]] = (PC[tempI[4:0]]==i_BTB_CMTPC);
	end
	
	wire			UpdateHit;
	wire	[4:0]	UpdateAddr;
LeadingOneCount32 BTB_LOC32_UpdateAddr(
	.i_LOC32_datain(UpdateList),
	.o_LOC32_vld(	UpdateHit),
	.o_LOC32_cnt(	UpdateAddr)
);

	//LookUp
	wire	[31:0]	LookUpList;
	begin:	lookupList
		reg	[7:0]	tempJ;
		for(tempJ=8'd0; tempJ<=8'd31; tempJ=tempJ+8'd1)
			assign	LookUpList[tempJ[4:0]] = (PC[tempJ[4:0]]==i_BTB_LUPPC);
	end
	
	wire	[31:0]	LookUpAddr
LeadingOneCount32 BTB_LOC32_LookUpAddr(
	.i_LOC32_datain(LookUpList),
	.o_LOC32_vld(	o_BTB_LUPhit),
	.o_LOC32_cnt(	LookUpAddr)
);
	assign	o_BTB_LUPcounter = counter[LookUpList];
	assign	o_BTB_LUPtarget = target[LookUpAddr];

always @(posedge i_BTB_clk)
	begin
		if(i_BTB_reset)
			begin: reset
				reg	[7:0]	tempK;
				for(tempK=8'd0; tempK<=8'd31; tempK=tempK+8'd1)
					begin
						vaild[tempK[4:0]]	<= 1'd0;
						PC[tempK[4:0]]		<= 32'd0;
						target[tempK[4:0]]	<= 32'd0;
						counter[tempK[4:0]]	<= 2'b00;
						likely[tempK[4:0]]	<= 1'd0;
						
						EnQueuePointer		<= 5'd0;
					end
			end
		else if(i_BTB_enable)
			begin
				//EnQueue
				if(i_BTB_PDCvalid)
					begin
						vaild[EnQueuePointer]	<= i_BTB_PDCvalid;
						likely[EnQueuePointer]	<= i_BTB_PDClikely;
						PC[EnQueuePointer]		<= i_BTB_PDCPC;
						target[EnQueuePointer]	<= i_BTB_PDCtarget;
						counter[EnQueuePointer]	<= 2'b10;
					end
				//Update
				if(i_BTB_CMTvalid && UpdateHit)
					begin
						target[UpdateAddr]	<= i_BTB_CMTtarget;
						if(i_BTB_CMTtaken==2'b11) counter[UpdateAddr] <= 2'b11;
						if(i_BTB_CMTtaken==2'b10) counter[UpdateAddr] <= counter[UpdateAddr] - 2'b01;
						if(i_BTB_CMTtaken==2'b01) counter[UpdateAddr] <= counter[UpdateAddr] + 2'b01;
						if(i_BTB_CMTtaken==2'b00) counter[UpdateAddr] <= 2'b00;
					end
			end
	end

endmodule